package com.whynot.frame.hystrix.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.exception.HystrixTimeoutException;
import org.springframework.stereotype.Service;

/**
 * 描    述:hystrix测试
 * 创建时间: 2018/12/21
 */
@Service
public class HystrixService {

    private static int count = 0;

    private static final String errorInfo = "请求失败，稍后再试";

    /**
     * 获取机器人回复内容
     * @return
     */
    @HystrixCommand(
            // 异常可自定义，或java 运行时异常
            // ignoreExceptions = ArithmeticException.class,
            // 设置回调函数，函数名必须和值一致
            fallbackMethod = "getFallbackReplyByRobot",
            //最少配置的必选参数，统一为服务名。在不指定ThreadPoolKey的情况下，字面值用于对不同依赖的线程池/信号区分
            groupKey = "hystrix_online_wxSec",
            //value=服务名+‘_’+方法名+'_command'，依赖隔离：每个CommandKey代表一个依赖抽象,相同的依赖要使用相同的CommandKey名称。
            commandKey = "hystrix_online_wxSec_getReplyByRobot_command",
            //value=key=服务名+‘_’+方法名+'_pool',资源隔离：虽然在业务上都是相同的组，但是需要在资源上做隔离时，可以使用HystrixThreadPoolKey区分。
            threadPoolKey = "hystrix_online_wxSec_getReplyByRobot_pool",
            commandProperties = {
                    //超时时间  天宫太慢了，还是设置稍微大点，2秒
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000"),

                    //调用线程允许请求HystrixCommand.GetFallback()的最大数量，默认10。超出时将会有异常抛出，注意：该项配置对于THREAD隔离模式也起作用
                    //一般来说fallback,应该是降级处理。不处理很复杂、耗时的业务，所以此值可以采用默认
                    @HystrixProperty(
                            name="fallback.isolation.semaphore.maxConcurrentRequests",value = "10"),

                    //设置熔断
                    @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
                    //断路器确定是否需要打开统计请求和错误数据的时候，具有一个时间范围即时间窗口，当断路器打开，
                    //设置滚动窗口中，10分钟出现异常数超过设置的值后，进行短路。默认是20个
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"),
                    //设置断路器打开的百分比条件，
                    //比如此处设置为60，在滚动窗口中发生了10次request，有7次发生了异常，超出设置值，则断路器就进入。应该不会有时间限制
                    //open状态，反之即closed
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),
                    //对主逻辑进行熔断之后，hystrix会启动休眠时间窗，此时降级逻辑会称为主逻辑，当休眠时间窗到期，断路
                    //器就进入half open状态
                    //尝试释放请求到原主逻辑上，就想之前描述的，成功则断路器闭合closed，失败则继续打开open，休眠时间
                    //窗将重新计时
                    //熔断多少秒后去尝试请求，以下设置休眠时间窗为5000毫秒。5秒之后，重新进行重试
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",
                            value = "5000"),
            },
            threadPoolProperties = {
                    //调整coreSize(线程池并发度) 由于现在机器人最高100，所以设置小点。
                    //需要根据jvm配置的线程开销，因为一个线程默认是申请1m的栈。如果jvm分配的内存较小，同时还有其他方法也很占内存。那么这里就不能设置过大，否则抢占内存资源太严重
                    @HystrixProperty(name = "coreSize", value = "80"),
                    //调整maximumSize
                    //这个值要比coreSize大。即使线程不够用超过了coreSize,也可以再“借”一些线程（总数不能超过maximumSize），但最终会还回去。
                    @HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize", value = "true"),
                    //由于现在机器人最高100，所以设置小点
                    //需要根据jvm配置的线程开销，因为一个线程默认是申请1m的栈。如果jvm分配的内存较小，同时还有其他方法也很占内存。那么这里就不能设置过大，否则抢占内存资源太严重
                    @HystrixProperty(name = "maximumSize", value = "90"),
                    //此属性允许 maximumSize 的配置生效，如果maximumSize 大于 coreSize 配置，则在 keepAliveTimeMinutes 时间后回收线程。默认1分钟
                    //@HystrixProperty(name = "keepAliveTimeMinutes",value = "2"),
                    //调整maxQueueSize
                    //在线程不够用时，可以采用排队的方式，因为排队增加了延时，所以此方式的副作用是会增加请求的延时。
                    //这个值需要合理设置，因为如果排队过长，那么排在后面的请求会超时，例如：超时时间是1000ms，单个请求的耗时大约100ms，那么排队数最大是10，再往后排的请求一定会超时。
                    //需要根据超时时间，以及对应接口的响应时间设置。比如20个请求进来了，超时时间是1000ms，单个请求的耗时大约100ms，那么排队数最大是10，那么剩余的10个请求肯定会超时
                    @HystrixProperty(name = "maxQueueSize", value = "20"),
                    //因为maxQueueSize值不能被动态修改，所有通过设置此值可以实现动态修改等待队列长度。即等待的队列的数量大于queueSizeRejectionThreshold时（但是没有达到maxQueueSize值），则开始拒绝后续的请求进入队列
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "25")
            }
    )

    public String getReplyByRobot() throws InterruptedException {
        System.out.println("开始执行");

        count++;
        if(count > 100 && count<110){
            Thread.sleep(3000);
        }
        if(count >110){
            int i=1/0;
        }
        return "执行正常";

    }


    /**
     * 失败之后降级措施
     * @return
     */
    public String getFallbackReplyByRobot(Throwable e){
        if(e instanceof HystrixTimeoutException){
            //超时
        }//断路
        //异常
        System.out.println("异常信息:"+e);
        // TODO: 2018/12/19 进行失败之后降级重试，重新丢到redis/队列，然后定时刷去重试
        System.out.println("进入fallback");
        return errorInfo;
    }
}